/***********************************************************************
	filename: 	CEGUIString.cpp
	created:	26/2/2004
	author:		Paul D Turner
	
	purpose:	Implements string class
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIString.h"

#include <iostream>

// Start of CEGUI namespace section
namespace CEGUI
{

// definition of 'no position' value
const String::size_type String::npos = (String::size_type)(-1);


//////////////////////////////////////////////////////////////////////////
// Destructor
//////////////////////////////////////////////////////////////////////////
String::~String(void)
{
	if (d_reserve > STR_QUICKBUFF_SIZE)
	{
		delete[] d_buffer;
	}
		if (d_encodedbufflen > 0)
	{
		delete[] d_encodedbuff;
	}
}

bool String::grow(String::size_type new_size)
{
    // check for too big
    if (max_size() <= new_size)
        std::length_error("Resulting CEGUI::String would be too big");

    // increase, as we always null-terminate the buffer.
    ++new_size;

    if (new_size > d_reserve)
    {
        utf32* temp = new utf32[new_size];

        if (d_reserve > STR_QUICKBUFF_SIZE)
        {
            memcpy(temp, d_buffer, (d_cplength + 1) * sizeof(utf32));
            delete[] d_buffer;
        }
        else
        {
            memcpy(temp, d_quickbuff, (d_cplength + 1) * sizeof(utf32));
        }

        d_buffer = temp;
        d_reserve = new_size;

        return true;
    }

    return false;
}

// perform re-allocation to remove wasted space.
void String::trim(void)
{
    String::size_type min_size = d_cplength + 1;

    // only re-allocate when not using quick-buffer, and when size can be trimmed
    if ((d_reserve > STR_QUICKBUFF_SIZE) && (d_reserve > min_size))
    {
            // see if we can trim to quick-buffer
        if (min_size <= STR_QUICKBUFF_SIZE)
        {
            memcpy(d_quickbuff, d_buffer, min_size * sizeof(utf32));
            delete[] d_buffer;
            d_reserve = STR_QUICKBUFF_SIZE;
        }
        // re-allocate buffer
        else
        {
            utf32* temp = new utf32[min_size];
            memcpy(temp, d_buffer, min_size * sizeof(utf32));
            delete[] d_buffer;
            d_buffer = temp;
            d_reserve = min_size;
        }

    }

}

// build an internal buffer with the string encoded as utf8 (remains valid until string is modified).
utf8* String::build_utf8_buff(void) const
{
    String::size_type buffsize = encoded_size(ptr(), d_cplength) + 1;

    if (buffsize > d_encodedbufflen) {

        if (d_encodedbufflen > 0)
        {
            delete[] d_encodedbuff;
        }

        d_encodedbuff = new utf8[buffsize];
        d_encodedbufflen = buffsize;
    }

    encode(ptr(), d_encodedbuff, buffsize, d_cplength);

    // always add a null at end
    d_encodedbuff[buffsize-1] = ((utf8)0);
    d_encodeddatlen = buffsize;

    return d_encodedbuff;
}

String::String( void )
{
	init();
}

String::String( const String& str )
{
	init();
	assign(str);
}

String::String( const String& str, String::size_type str_idx, String::size_type str_num /*= npos*/ )
{
	init();
	assign(str, str_idx, str_num);
}

String::String( const std::string& std_str )
{
	init();
	assign(std_str);
}

String::String( const std::string& std_str, String::size_type str_idx, String::size_type str_num /*= npos*/ )
{
	init();
	assign(std_str, str_idx, str_num);
}

String::String( const utf8* utf8_str )
{
	init();
	assign(utf8_str);
}

String::String( const utf8* utf8_str, String::size_type chars_len )
{
	init();
	assign(utf8_str, chars_len);
}

String::String( String::size_type num, utf32 code_point )
{
	init();
	assign(num, code_point);
}

String::String( String::const_iterator iter_beg, String::const_iterator iter_end )
{
	init();
	append(iter_beg, iter_end);
}

String::String( const char* cstr )
{
	init();
	assign(cstr);
}

String::String( const wchar_t* cstr )
{
	init();
	assign(cstr);
}

String::String( const char* chars, String::size_type chars_len )
{
	init();
	assign(chars, chars_len);
}

String::size_type String::size( void ) const
{
	return d_cplength;
}

String::size_type String::length( void ) const
{
	return d_cplength;
}

bool String::empty( void ) const
{
	return	(d_cplength == 0);
}

String::size_type String::max_size( void )
{
	return (((String::size_type)-1) / sizeof(utf32));
}

String::size_type String::capacity( void ) const
{
	return d_reserve - 1;
}

void String::reserve( String::size_type num /*= 0*/ )
{
	if (num == 0)
		trim();
	else
		grow(num);
}

int String::compare( const String& str ) const
{
	return compare(0, d_cplength, str);
}

int String::compare( String::size_type idx, String::size_type len, const String& str, String::size_type str_idx /*= 0*/, String::size_type str_len /*= npos*/ ) const
{
	if ((d_cplength < idx) || (str.d_cplength < str_idx))
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if ((len == npos) || (idx + len > d_cplength))
		len = d_cplength - idx;

	if ((str_len == npos) || (str_idx + str_len > str.d_cplength))
		str_len = str.d_cplength - str_idx;

	int val = (len == 0) ? 0 : utf32_comp_utf32(&ptr()[idx], &str.ptr()[str_idx], (len < str_len) ? len : str_len);

	return (val != 0) ? ((val < 0) ? -1 : 1) : (len < str_len) ? -1 : (len == str_len) ? 0 : 1;
}

int String::compare( const std::string& std_str ) const
{
	return compare(0, d_cplength, std_str);
}

int String::compare( String::size_type idx, String::size_type len, const std::string& std_str, String::size_type str_idx /*= 0*/, String::size_type str_len /*= npos*/ ) const
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (std_str.size() < str_idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for std::string"));

	if ((len == npos) || (idx + len > d_cplength))
		len = d_cplength - idx;

	if ((str_len == npos) || (str_idx + str_len > std_str.size()))
		str_len = (String::size_type)std_str.size() - str_idx;

	int val = (len == 0) ? 0 : utf32_comp_char(&ptr()[idx], &std_str.c_str()[str_idx], (len < str_len) ? len : str_len);

	return (val != 0) ? ((val < 0) ? -1 : 1) : (len < str_len) ? -1 : (len == str_len) ? 0 : 1;
}

int String::compare( const utf8* utf8_str ) const
{
	return compare(0, d_cplength, utf8_str, encoded_size(utf8_str));
}

int String::compare( String::size_type idx, String::size_type len, const utf8* utf8_str ) const
{
	return compare(idx, len, utf8_str, encoded_size(utf8_str));
}

int String::compare( String::size_type idx, String::size_type len, const utf8* utf8_str, String::size_type str_cplen ) const
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (str_cplen == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	if ((len == npos) || (idx + len > d_cplength))
		len = d_cplength - idx;

	int val = (len == 0) ? 0 : utf32_comp_utf8(&ptr()[idx], utf8_str, (len < str_cplen) ? len : str_cplen);

	return (val != 0) ? ((val < 0) ? -1 : 1) : (len < str_cplen) ? -1 : (len == str_cplen) ? 0 : 1;
}

int String::compare( const char* cstr ) const
{
	return compare(0, d_cplength, cstr, strlen(cstr));
}

int String::compare( String::size_type idx, String::size_type len, const char* cstr ) const
{
	return compare(idx, len, cstr, strlen(cstr));
}

int String::compare( String::size_type idx, String::size_type len, const char* chars, String::size_type chars_len ) const
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if ((len == npos) || (idx + len > d_cplength))
		len = d_cplength - idx;

	int val = (len == 0) ? 0 : utf32_comp_char(&ptr()[idx], chars, (len < chars_len) ? len : chars_len);

	return (val != 0) ? ((val < 0) ? -1 : 1) : (len < chars_len) ? -1 : (len == chars_len) ? 0 : 1;
}

const char* String::c_str( void ) const
{
	return (const char*)build_utf8_buff();
}

const utf8* String::data( void ) const
{
	return build_utf8_buff();
}

utf32* String::ptr( void )
{
	return (d_reserve > STR_QUICKBUFF_SIZE) ? d_buffer : d_quickbuff;
}

const utf32* String::ptr( void ) const
{
	return (d_reserve > STR_QUICKBUFF_SIZE) ? d_buffer : d_quickbuff;
}

String::size_type String::copy( utf8* buf, String::size_type len /*= npos*/, String::size_type idx /*= 0*/ ) const
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (len == npos)
		len = d_cplength;

	return encode(&ptr()[idx], buf, npos, len);
}

String::size_type String::utf8_stream_len( String::size_type num /*= npos*/, String::size_type idx /*= 0*/ ) const
{
	using namespace std;

	if (d_cplength < idx)
		CEGUI_THROW(out_of_range("Index was out of range for CEGUI::String object"));

	String::size_type	maxlen = d_cplength - idx;

	return encoded_size(&ptr()[idx], ceguimin(num, maxlen));
}

String& String::assign( const String& str, String::size_type str_idx /*= 0*/, String::size_type str_num /*= npos*/ )
{
	if (str.d_cplength < str_idx)
		CEGUI_THROW(std::out_of_range("Index was out of range for CEGUI::String object"));

	if ((str_num == npos) || (str_num > str.d_cplength - str_idx))
		str_num = str.d_cplength - str_idx;

	grow(str_num);
	setlen(str_num);
	memcpy(ptr(), &str.ptr()[str_idx], str_num * sizeof(utf32));

	return *this;
}

String& String::assign( const std::string& std_str, String::size_type str_idx /*= 0*/, String::size_type str_num /*= npos*/ )
{
	if (std_str.size() < str_idx)
		CEGUI_THROW(std::out_of_range("Index was out of range for std::string object"));

	if ((str_num == npos) || (str_num > (String::size_type)std_str.size() - str_idx))
		str_num = (String::size_type)std_str.size() - str_idx;

	grow(str_num);
	setlen(str_num);

	while(str_num--)
	{
		((*this)[str_num]) = static_cast<utf32>(static_cast<unsigned char>(std_str[str_num + str_idx]));
	}

	return *this;
}

String& String::assign( const utf8* utf8_str )
{
	return assign(utf8_str, utf_length(utf8_str));
}

String& String::assign( const utf8* utf8_str, String::size_type str_num )
{
	if (str_num == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	String::size_type enc_sze = encoded_size(utf8_str, str_num);

	grow(enc_sze);
	encode(utf8_str, ptr(), d_reserve, str_num);
	setlen(enc_sze);
	return *this;
}

String& String::assign( String::size_type num, utf32 code_point )
{
	if (num == npos)
		CEGUI_THROW(std::length_error("Code point count can not be 'npos'"));

	grow(num);
	setlen(num);
	utf32* p = ptr();

	while(num--)
		*p++ = code_point;

	return *this;
}

String& String::assign( const char* cstr )
{
	return assign(cstr, strlen(cstr));
}

String& String::assign( const char* chars, String::size_type chars_len )
{
	grow(chars_len);
	utf32* pt = ptr();

	for (String::size_type i = 0; i < chars_len; ++i)
	{
		*pt++ = static_cast<utf32>(static_cast<unsigned char>(*chars++));
	}

	setlen(chars_len);
	return *this;
}

String& String::assign( const wchar_t* cstr )
{
	return assign(cstr, wcslen(cstr));
}

String& String::assign( const wchar_t* chars, String::size_type chars_len )
{
	grow(chars_len);
	utf32* pt = ptr();

	for (String::size_type i = 0; i < chars_len; ++i)
	{
		*pt++ = static_cast<utf32>(*chars++);
	}

	setlen(chars_len);
	return *this;
}

void String::swap( String& str )
{
	String::size_type	temp_len	= d_cplength;
	d_cplength = str.d_cplength;
	str.d_cplength = temp_len;

	String::size_type	temp_res	= d_reserve;
	d_reserve = str.d_reserve;
	str.d_reserve = temp_res;

	utf32*		temp_buf	= d_buffer;
	d_buffer = str.d_buffer;
	str.d_buffer = temp_buf;

	// see if we need to swap 'quick buffer' data
	if (temp_res <= STR_QUICKBUFF_SIZE)
	{
		utf32		temp_qbf[STR_QUICKBUFF_SIZE];

		memcpy(temp_qbf, d_quickbuff, STR_QUICKBUFF_SIZE * sizeof(utf32));
		memcpy(d_quickbuff, str.d_quickbuff, STR_QUICKBUFF_SIZE * sizeof(utf32));
		memcpy(str.d_quickbuff, temp_qbf, STR_QUICKBUFF_SIZE * sizeof(utf32));
	}
}

String& String::append( const String& str, String::size_type str_idx /*= 0*/, String::size_type str_num /*= npos*/ )
{
	if (str.d_cplength < str_idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if ((str_num == npos) || (str_num > str.d_cplength - str_idx))
		str_num = str.d_cplength - str_idx;

	grow(d_cplength + str_num);
	memcpy(&ptr()[d_cplength], &str.ptr()[str_idx], str_num * sizeof(utf32));
	setlen(d_cplength + str_num);
	return *this;
}

String& String::append( const std::string& std_str, String::size_type str_idx /*= 0*/, String::size_type str_num /*= npos*/ )
{
	if (std_str.size() < str_idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for std::string"));

	if ((str_num == npos) || (str_num > (String::size_type)std_str.size() - str_idx))
		str_num = (String::size_type)std_str.size() - str_idx;

	String::size_type newsze = d_cplength + str_num;

	grow(newsze);
	utf32* pt = &ptr()[newsze-1];

	while(str_num--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(std_str[str_num]));

	setlen(newsze);
	return *this;
}

String& String::append( const utf8* utf8_str )
{
	return append(utf8_str, utf_length(utf8_str));
}

String& String::append( const utf8* utf8_str, String::size_type len )
{
	if (len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	String::size_type encsz = encoded_size(utf8_str, len);
	String::size_type newsz = d_cplength + encsz;

	grow(newsz);
	encode(utf8_str, &ptr()[d_cplength], encsz, len);
	setlen(newsz);

	return *this;
}

String& String::append( String::size_type num, utf32 code_point )
{
	if (num == npos)
		CEGUI_THROW(std::length_error("Code point count can not be 'npos'"));

	String::size_type newsz = d_cplength + num;
	grow(newsz);

	utf32* p = &ptr()[d_cplength];

	while(num--)
		*p++ = code_point;

	setlen(newsz);

	return *this;
}

String& String::append( String::const_iterator iter_beg, String::const_iterator iter_end )
{
	return replace(end(), end(), iter_beg, iter_end);
}

String& String::append( const char* cstr )
{
	return append(cstr, strlen(cstr));
}

String& String::append( const char* chars, String::size_type chars_len )
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	String::size_type newsz = d_cplength + chars_len;

	grow(newsz);

	utf32* pt = &ptr()[newsz-1];

	while(chars_len--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(chars[chars_len]));

	setlen(newsz);

	return *this;
}




void String::push_back( utf32 code_point )
{
	append(1, code_point);
}

String& String::insert( String::size_type idx, const String& str )
{
	return insert(idx, str, 0, npos);
}

String& String::insert( String::size_type idx, const String& str, String::size_type str_idx, String::size_type str_num )
{
	if ((d_cplength < idx) || (str.d_cplength < str_idx))
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if ((str_num == npos) || (str_num > str.d_cplength - str_idx))
		str_num = str.d_cplength - str_idx;

	String::size_type newsz = d_cplength + str_num;
	grow(newsz);
	memmove(&ptr()[idx + str_num], &ptr()[idx], (d_cplength - idx) * sizeof(utf32));
	memcpy(&ptr()[idx], &str.ptr()[str_idx], str_num * sizeof(utf32));
	setlen(newsz);

	return *this;
}

String& String::insert( String::size_type idx, const std::string& std_str )
{
	return insert(idx, std_str, 0, npos);
}

String& String::insert( String::size_type idx, const std::string& std_str, String::size_type str_idx, String::size_type str_num )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (std_str.size() < str_idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for std::string"));

	if ((str_num == npos) || (str_num > (String::size_type)std_str.size() - str_idx))
		str_num = (String::size_type)std_str.size() - str_idx;

	String::size_type newsz = d_cplength + str_num;
	grow(newsz);

	memmove(&ptr()[idx + str_num], &ptr()[idx], (d_cplength - idx) * sizeof(utf32));

	utf32* pt = &ptr()[idx + str_num - 1];

	while(str_num--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(std_str[str_idx + str_num]));

	setlen(newsz);

	return *this;
}

String& String::insert( String::size_type idx, const utf8* utf8_str )
{
	return insert(idx, utf8_str, utf_length(utf8_str));
}

String& String::insert( String::size_type idx, const utf8* utf8_str, String::size_type len )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (len == npos)
		CEGUI_THROW(std::length_error("Length of utf8 encoded string can not be 'npos'"));

	String::size_type encsz = encoded_size(utf8_str, len);
	String::size_type newsz = d_cplength + encsz;

	grow(newsz);
	memmove(&ptr()[idx + encsz], &ptr()[idx], (d_cplength - idx) * sizeof(utf32));
	encode(utf8_str, &ptr()[idx], encsz, len);
	setlen(newsz);

	return *this;
}

String& String::insert( String::size_type idx, String::size_type num, utf32 code_point )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (num == npos)
		CEGUI_THROW(std::length_error("Code point count can not be 'npos'"));

	String::size_type newsz = d_cplength + num;
	grow(newsz);

	memmove(&ptr()[idx + num], &ptr()[idx], (d_cplength - idx) * sizeof(utf32));

	utf32* pt = &ptr()[idx + num - 1];

	while(num--)
		*pt-- = code_point;

	setlen(newsz);

	return *this;
}

void String::insert( String::iterator pos, String::size_type num, utf32 code_point )
{
	insert(safe_iter_dif(pos, begin()), num, code_point);
}

String::iterator String::insert( String::iterator pos, utf32 code_point )
{
	insert(pos, 1, code_point);
	return pos;
}

void String::insert( String::iterator iter_pos, String::const_iterator iter_beg, String::const_iterator iter_end )
{
	replace(iter_pos, iter_pos, iter_beg, iter_end);
}

String& String::insert( String::size_type idx, const char* cstr )
{
	return insert(idx, cstr, strlen(cstr));
}

String& String::insert( String::size_type idx, const char* chars, String::size_type chars_len )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length of char array can not be 'npos'"));

	String::size_type newsz = d_cplength + chars_len;

	grow(newsz);
	memmove(&ptr()[idx + chars_len], &ptr()[idx], (d_cplength - idx) * sizeof(utf32));

	utf32* pt = &ptr()[idx + chars_len - 1];

	while(chars_len--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(chars[chars_len]));

	setlen(newsz);

	return *this;
}

void String::clear( void )
{
	setlen(0);
	trim();
}

String& String::erase( void )
{
	clear();
	return *this;
}

String& String::erase( String::size_type idx )
{
	return erase(idx, 1);
}

String& String::erase( String::size_type idx, String::size_type len )
{
	// cover the no-op case.
	if (len == 0)
		return *this;

	if (d_cplength <= idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (len == npos)
		len = d_cplength - idx;

	String::size_type newsz = d_cplength - len;

	memmove(&ptr()[idx], &ptr()[idx + len], (d_cplength - idx - len) * sizeof(utf32));
	setlen(newsz);
	return	*this;
}

String& String::erase( String::iterator pos )
{
	return erase(safe_iter_dif(pos, begin()), 1);
}

String& String::erase( String::iterator iter_beg, String::iterator iter_end )
{
	return erase(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg));
}

void String::resize( String::size_type num )
{
	resize(num, utf32());
}

void String::resize( String::size_type num, utf32 code_point )
{
	if (num < d_cplength)
	{
		setlen(num);
	}
	else
	{
		append(num - d_cplength, code_point);
	}
}

String& String::replace( String::size_type idx, String::size_type len, const String& str )
{
	return replace(idx, len, str, 0, npos);
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, const String& str )
{
	return replace(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg), str, 0, npos);
}

String& String::replace( String::size_type idx, String::size_type len, const String& str, String::size_type str_idx, String::size_type str_num )
{
	if ((d_cplength < idx) || (str.d_cplength < str_idx))
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (((str_idx + str_num) > str.d_cplength) || (str_num == npos))
		str_num = str.d_cplength - str_idx;

	if (((len + idx) > d_cplength) || (len == npos))
		len = d_cplength - idx;

	String::size_type newsz = d_cplength + str_num - len;

	grow(newsz);

	if ((idx + len) < d_cplength)
		memmove(&ptr()[idx + str_num], &ptr()[len + idx], (d_cplength - idx - len) * sizeof(utf32));

	memcpy(&ptr()[idx], &str.ptr()[str_idx], str_num * sizeof(utf32));
	setlen(newsz);

	return *this;
}

String& String::replace( String::size_type idx, String::size_type len, const std::string& std_str )
{
	return replace(idx, len, std_str, 0, npos);
}

String& String::replace( String::size_type idx, String::size_type len, const std::string& std_str, String::size_type str_idx, String::size_type str_num )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (std_str.size() < str_idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for std::string"));

	if (((str_idx + str_num) > std_str.size()) || (str_num == npos))
		str_num = (String::size_type)std_str.size() - str_idx;

	if (((len + idx) > d_cplength) || (len == npos))
		len = d_cplength - idx;

	String::size_type newsz = d_cplength + str_num - len;

	grow(newsz);

	if ((idx + len) < d_cplength)
		memmove(&ptr()[idx + str_num], &ptr()[len + idx], (d_cplength - idx - len) * sizeof(utf32));

	utf32* pt = &ptr()[idx + str_num - 1];

	while (str_num--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(std_str[str_idx + str_num]));

	setlen(newsz);

	return *this;
}

String& String::replace( String::size_type idx, String::size_type len, const utf8* utf8_str )
{
	return replace(idx, len, utf8_str, utf_length(utf8_str));
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, const utf8* utf8_str )
{
	return replace(iter_beg, iter_end, utf8_str, utf_length(utf8_str));
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, const utf8* utf8_str, String::size_type str_len )
{
	return replace(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg), utf8_str, str_len);
}

String& String::replace( String::size_type idx, String::size_type len, String::size_type num, utf32 code_point )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (num == npos)
		CEGUI_THROW(std::length_error("Code point count can not be 'npos'"));

	if (((len + idx) > d_cplength) || (len == npos))
		len = d_cplength - idx;

	String::size_type newsz = d_cplength + num - len;

	grow(newsz);

	if ((idx + len) < d_cplength)
		memmove(&ptr()[idx + num], &ptr()[len + idx], (d_cplength - idx - len) * sizeof(utf32));

	utf32* pt = &ptr()[idx + num - 1];

	while (num--)
		*pt-- = code_point;

	setlen(newsz);

	return *this;
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, String::size_type num, utf32 code_point )
{
	return replace(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg), num, code_point);
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, String::const_iterator iter_newBeg, String::const_iterator iter_newEnd )
{
	if (iter_newBeg == iter_newEnd)
	{
		erase(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg));
	}
	else
	{
		String::size_type str_len = safe_iter_dif(iter_newEnd, iter_newBeg);
		String::size_type idx = safe_iter_dif(iter_beg, begin());
		String::size_type len = safe_iter_dif(iter_end, iter_beg);

		if ((len + idx) > d_cplength)
			len = d_cplength - idx;

		String::size_type newsz = d_cplength + str_len - len;

		grow(newsz);

		if ((idx + len) < d_cplength)
			memmove(&ptr()[idx + str_len], &ptr()[len + idx], (d_cplength - idx - len) * sizeof(utf32));

		memcpy(&ptr()[idx], iter_newBeg.d_ptr, str_len * sizeof(utf32));
		setlen(newsz);
	}

	return *this;
}

String& String::replace( String::size_type idx, String::size_type len, const char* cstr )
{
	return replace(idx, len, cstr, strlen(cstr));
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, const char* cstr )
{
	return replace(iter_beg, iter_end, cstr, strlen(cstr));
}

String& String::replace( String::size_type idx, String::size_type len, const char* chars, String::size_type chars_len )
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for CEGUI::String"));

	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for the char array can not be 'npos'"));

	if (((len + idx) > d_cplength) || (len == npos))
		len = d_cplength - idx;

	String::size_type newsz = d_cplength + chars_len - len;

	grow(newsz);

	if ((idx + len) < d_cplength)
		memmove(&ptr()[idx + chars_len], &ptr()[len + idx], (d_cplength - idx - len) * sizeof(utf32));

	utf32* pt = &ptr()[idx + chars_len - 1];

	while (chars_len--)
		*pt-- = static_cast<utf32>(static_cast<unsigned char>(chars[chars_len]));

	setlen(newsz);
	return *this;
}

String& String::replace( String::iterator iter_beg, String::iterator iter_end, const char* chars, String::size_type chars_len )
{
	return replace(safe_iter_dif(iter_beg, begin()), safe_iter_dif(iter_end, iter_beg), chars, chars_len);
}

String::size_type String::find( utf32 code_point, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		while (idx < d_cplength)
		{
			if (*pt++ == code_point)
				return idx;

			++idx;
		}

	}

	return npos;
}

String::size_type String::find( const String& str, String::size_type idx /*= 0*/ ) const
{
	if ((str.d_cplength == 0) && (idx < d_cplength))
		return idx;

	if (idx < d_cplength)
	{
		// loop while search string could fit in to search area
		while (d_cplength - idx >= str.d_cplength)
		{
			if (0 == compare(idx, str.d_cplength, str))
				return idx;

			++idx;
		}

	}

	return npos;
}

String::size_type String::find( const std::string& std_str, String::size_type idx /*= 0*/ ) const
{
	std::string::size_type sze = std_str.size();

	if ((sze == 0) && (idx < d_cplength))
		return idx;

	if (idx < d_cplength)
	{
		// loop while search string could fit in to search area
		while (d_cplength - idx >= sze)
		{
			if (0 == compare(idx, (String::size_type)sze, std_str))
				return idx;

			++idx;
		}

	}

	return npos;
}

String::size_type String::find( const utf8* utf8_str, String::size_type idx /*= 0*/ ) const
{
	return find(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::find( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	String::size_type sze = encoded_size(utf8_str, str_len);

	if ((sze == 0) && (idx < d_cplength))
		return idx;

	if (idx < d_cplength)
	{
		// loop while search string could fit in to search area
		while (d_cplength - idx >= sze)
		{
			if (0 == compare(idx, sze, utf8_str, sze))
				return idx;

			++idx;
		}

	}

	return npos;
}

String::size_type String::find( const char* cstr, String::size_type idx /*= 0*/ ) const
{
	return find(cstr, idx, strlen(cstr));
}

String::size_type String::find( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if ((chars_len == 0) && (idx < d_cplength))
		return idx;

	if (idx < d_cplength)
	{
		// loop while search string could fit in to search area
		while (d_cplength - idx >= chars_len)
		{
			if (0 == compare(idx, chars_len, chars, chars_len))
				return idx;

			++idx;
		}

	}

	return npos;
}







String::size_type String::rfind( utf32 code_point, String::size_type idx /*= npos*/ ) const
{
	if (idx >= d_cplength)
		idx = d_cplength - 1;

	if (d_cplength > 0)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (*pt-- == code_point)
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::rfind( const String& str, String::size_type idx /*= npos*/ ) const
{
	if (str.d_cplength == 0)
		return (idx < d_cplength) ? idx : d_cplength;

	if (str.d_cplength <= d_cplength)
	{
		if (idx > (d_cplength - str.d_cplength))
			idx = d_cplength - str.d_cplength;

		do
		{
			if (0 == compare(idx, str.d_cplength, str))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::rfind( const std::string& std_str, String::size_type idx /*= npos*/ ) const
{
	std::string::size_type sze = std_str.size();

	if (sze == 0)
		return (idx < d_cplength) ? idx : d_cplength;

	if (sze <= d_cplength)
	{
		if (idx > (d_cplength - sze))
			idx = d_cplength - sze;

		do
		{
			if (0 == compare(idx, (String::size_type)sze, std_str))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::rfind( const utf8* utf8_str, String::size_type idx /*= npos*/ ) const
{
	return rfind(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::rfind( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	String::size_type sze = encoded_size(utf8_str, str_len);

	if (sze == 0)
		return (idx < d_cplength) ? idx : d_cplength;

	if (sze <= d_cplength)
	{
		if (idx > (d_cplength - sze))
			idx = d_cplength - sze;

		do
		{
			if (0 == compare(idx, sze, utf8_str, sze))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::rfind( const char* cstr, String::size_type idx /*= npos*/ ) const
{
	return rfind(cstr, idx, strlen(cstr));
}

String::size_type String::rfind( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if (chars_len == 0)
		return (idx < d_cplength) ? idx : d_cplength;

	if (chars_len <= d_cplength)
	{
		if (idx > (d_cplength - chars_len))
			idx = d_cplength - chars_len;

		do
		{
			if (0 == compare(idx, chars_len, chars, chars_len))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_first_of( const String& str, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != str.find(*pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_of( const std::string& std_str, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(std_str, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_of( const utf8* utf8_str, String::size_type idx /*= 0*/ ) const
{
	return find_first_of(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::find_first_of( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	if (idx < d_cplength)
	{
		String::size_type encsze = encoded_size(utf8_str, str_len);

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(utf8_str, encsze, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_of( utf32 code_point, String::size_type idx /*= 0*/ ) const
{
	return find(code_point, idx);
}

String::size_type String::find_first_of( const char* cstr, String::size_type idx /*= 0*/ ) const
{
	return find_first_of(cstr, idx, strlen(cstr));
}

String::size_type String::find_first_of( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(chars, chars_len, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}







String::size_type String::find_first_not_of( const String& str, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == str.find(*pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_not_of( const std::string& std_str, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(std_str, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_not_of( const utf8* utf8_str, String::size_type idx /*= 0*/ ) const
{
	return find_first_not_of(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::find_first_not_of( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	if (idx < d_cplength)
	{
		String::size_type encsze = encoded_size(utf8_str, str_len);

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(utf8_str, encsze, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_first_not_of( utf32 code_point, String::size_type idx /*= 0*/ ) const
{
	if (idx < d_cplength)
	{
		do
		{
			if ((*this)[idx] != code_point)
				return idx;

		} while(idx++ < d_cplength);

	}

	return npos;
}

String::size_type String::find_first_not_of( const char* cstr, String::size_type idx /*= 0*/ ) const
{
	return find_first_not_of(cstr, idx, strlen(cstr));
}

String::size_type String::find_first_not_of( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if (idx < d_cplength)
	{
		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(chars, chars_len, *pt++))
				return idx;

		} while (++idx != d_cplength);

	}

	return npos;
}

String::size_type String::find_last_of( const String& str, String::size_type idx /*= npos*/ ) const
{
	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != str.find(*pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_of( const std::string& std_str, String::size_type idx /*= npos*/ ) const
{
	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(std_str, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_of( const utf8* utf8_str, String::size_type idx /*= npos*/ ) const
{
	return find_last_of(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::find_last_of( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		String::size_type encsze = encoded_size(utf8_str, str_len);

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(utf8_str, encsze, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_of( utf32 code_point, String::size_type idx /*= npos*/ ) const
{
	return rfind(code_point, idx);
}

String::size_type String::find_last_of( const char* cstr, String::size_type idx /*= npos*/ ) const
{
	return find_last_of(cstr, idx, strlen(cstr));
}

String::size_type String::find_last_of( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos != find_codepoint(chars, chars_len, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}







String::size_type String::find_last_not_of( const String& str, String::size_type idx /*= npos*/ ) const
{
	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == str.find(*pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_not_of( const std::string& std_str, String::size_type idx /*= npos*/ ) const
{
	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(std_str, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_not_of( const utf8* utf8_str, String::size_type idx /*= npos*/ ) const
{
	return find_last_not_of(utf8_str, idx, utf_length(utf8_str));
}

String::size_type String::find_last_not_of( const utf8* utf8_str, String::size_type idx, String::size_type str_len ) const
{
	if (str_len == npos)
		CEGUI_THROW(std::length_error("Length for utf8 encoded string can not be 'npos'"));

	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		String::size_type encsze = encoded_size(utf8_str, str_len);

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(utf8_str, encsze, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_not_of( utf32 code_point, String::size_type idx /*= npos*/ ) const
{
	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		do
		{
			if ((*this)[idx] != code_point)
				return idx;

		} while(idx-- != 0);

	}

	return npos;
}

String::size_type String::find_last_not_of( const char* cstr, String::size_type idx /*= npos*/ ) const
{
	return find_last_not_of(cstr, idx, strlen(cstr));
}

String::size_type String::find_last_not_of( const char* chars, String::size_type idx, String::size_type chars_len ) const
{
	if (chars_len == npos)
		CEGUI_THROW(std::length_error("Length for char array can not be 'npos'"));

	if (d_cplength > 0)
	{
		if (idx >= d_cplength)
			idx = d_cplength - 1;

		const utf32* pt = &ptr()[idx];

		do
		{
			if (npos == find_codepoint(chars, chars_len, *pt--))
				return idx;

		} while (idx-- != 0);

	}

	return npos;
}

CEGUI::String String::substr( String::size_type idx /*= 0*/, String::size_type len /*= npos*/ ) const
{
	if (d_cplength < idx)
		CEGUI_THROW(std::out_of_range("Index is out of range for this CEGUI::String"));

	return String(*this, idx, len);
}

void String::setlen( String::size_type len )
{
	d_cplength = len;
	ptr()[len] = (utf32)(0);
}

void String::init( void )
{
	d_reserve			= STR_QUICKBUFF_SIZE;
	d_encodedbuff		= 0;
	d_encodedbufflen	= 0;
	d_encodeddatlen		= 0;
	d_buffer            = 0;
	setlen(0);
}

bool String::inside( utf32* inptr )
{
	if (inptr < ptr() || ptr() + d_cplength <= inptr)
		return false;
	else
		return true;
}

String::size_type String::safe_iter_dif( const String::const_iterator& iter1, const String::const_iterator& iter2 ) const
{
	return (iter1.d_ptr == 0) ? 0 : (iter1 - iter2);
}

String::size_type String::encode( const utf32* src, utf8* dest, String::size_type dest_len, String::size_type src_len /*= 0*/ ) const
{
	// count length for null terminated source...
	if (src_len == 0)
	{
		src_len = utf_length(src);
	}

	String::size_type destCapacity = dest_len;

	// while there is data in the source buffer,
	for (uint idx = 0; idx < src_len; ++idx)
	{
		utf32	cp = src[idx];

		// check there is enough destination buffer to receive this encoded unit (exit loop & return if not)
		if (destCapacity < encoded_size(cp))
		{
			break;
		}

		if (cp < 0x80)
		{
			*dest++ = (utf8)cp;
			--destCapacity;
		}
		else if (cp < 0x0800)
		{
			*dest++ = (utf8)((cp >> 6) | 0xC0);
			*dest++ = (utf8)((cp & 0x3F) | 0x80);
			destCapacity -= 2;
		}
		else if (cp < 0x10000)
		{
			*dest++ = (utf8)((cp >> 12) | 0xE0);
			*dest++ = (utf8)(((cp >> 6) & 0x3F) | 0x80);
			*dest++ = (utf8)((cp & 0x3F) | 0x80);
			destCapacity -= 3;
		}
		else
		{
			// 				*dest++ = (utf8)((cp >> 18) | 0xF0);
			// 				*dest++ = (utf8)(((cp >> 12) & 0x3F) | 0x80);
			// 				*dest++ = (utf8)(((cp >> 6) & 0x3F) | 0x80);
			// 				*dest++ = (utf8)((cp & 0x3F) | 0x80);
			// 				destCapacity -= 4;
		}

	}

	return dest_len - destCapacity;
}

String::size_type String::encode( const utf8* src, utf32* dest, String::size_type dest_len, String::size_type src_len /*= 0*/ ) const
{
	// count length for null terminated source...
	if (src_len == 0)
	{
		src_len = utf_length(src);
	}

	String::size_type destCapacity = dest_len;

	// while there is data in the source buffer, and space in the dest buffer
	for (uint idx = 0; ((idx < src_len) && (destCapacity > 0));)
	{
		utf32	cp;
		utf8	cu = src[idx++];

		if (cu < 0x80)
		{
			cp = (utf32)(cu);
		}
		else if (cu < 0xE0)
		{
			cp = ((cu & 0x1F) << 6);
			cp |= (src[idx++] & 0x3F);
		}
		else if (cu < 0xF0)
		{
			cp = ((cu & 0x0F) << 12);
			cp |= ((src[idx++] & 0x3F) << 6);
			cp |= (src[idx++] & 0x3F);
		}
		else
		{
			cp = ((cu & 0x07) << 18);
			cp |= ((src[idx++] & 0x3F) << 12);
			cp |= ((src[idx++] & 0x3F) << 6);
			cp |= (src[idx++] & 0x3F);
		}

		*dest++ = cp;
		--destCapacity;
	}

	return dest_len - destCapacity;
}

String::size_type String::encoded_size( utf32 code_point ) const
{
	if (code_point < 0x80)
		return 1;
	else if (code_point < 0x0800)
		return 2;
	else if (code_point < 0x10000)
		return 3;
	else
		return 4;
}

String::size_type String::encoded_size( const utf32* buf ) const
{
	return encoded_size(buf, utf_length(buf));
}

String::size_type String::encoded_size( const utf32* buf, String::size_type len ) const
{
	String::size_type count = 0;

	while (len--)
	{
		count += encoded_size(*buf++);
	}

	return count;
}

String::size_type String::encoded_size( const utf8* buf ) const
{
	return encoded_size(buf, utf_length(buf));
}

String::size_type String::encoded_size( const utf8* buf, String::size_type len ) const
{
	utf8 tcp;
	String::size_type count = 0;

	while (len--)
	{
		tcp = *buf++;
		++count;
		String::size_type size = 0;

		if (tcp < 0x80)
		{
		}
		else if (tcp < 0xE0)
		{
			size = 1;
			++buf;
		}
		else if (tcp < 0xF0)
		{
			size = 2;
			buf += 2;
		}
		else
		{
			size = 3;
			buf += 3;
		}

		if (len >= size)
			len -= size;
		else 
			break;
	}

	return count;
}

String::size_type String::utf_length( const utf8* utf8_str ) const
{
	String::size_type cnt = 0;
	while (*utf8_str++)
		cnt++;

	return cnt;
}

String::size_type String::utf_length( const utf32* utf32_str ) const
{
	String::size_type cnt = 0;
	while (*utf32_str++)
		cnt++;

	return cnt;
}

int String::utf32_comp_utf32( const utf32* buf1, const utf32* buf2, String::size_type cp_count ) const
{
	if (!cp_count)
		return 0;

	while ((--cp_count) && (*buf1 == *buf2))
		buf1++, buf2++;

	return *buf1 - *buf2;
}

int String::utf32_comp_char( const utf32* buf1, const char* buf2, String::size_type cp_count ) const
{
	if (!cp_count)
		return 0;

	while ((--cp_count) && (*buf1 == static_cast<utf32>(static_cast<unsigned char>(*buf2))))
		buf1++, buf2++;

	return *buf1 - static_cast<utf32>(static_cast<unsigned char>(*buf2));
}

int String::utf32_comp_utf8( const utf32* buf1, const utf8* buf2, String::size_type cp_count ) const
{
	if (!cp_count)
		return 0;

	utf32	cp;
	utf8	cu;

	do
	{
		cu = *buf2++;

		if (cu < 0x80)
		{
			cp = (utf32)(cu);
		}
		else if (cu < 0xE0)
		{
			cp = ((cu & 0x1F) << 6);
			cp |= (*buf2++ & 0x3F);
		}
		else if (cu < 0xF0)
		{
			cp = ((cu & 0x0F) << 12);
			cp |= ((*buf2++ & 0x3F) << 6);
			cp |= (*buf2++ & 0x3F);
		}
		else
		{
			cp = ((cu & 0x07) << 18);
			cp |= ((*buf2++ & 0x3F) << 12);
			cp |= ((*buf2++ & 0x3F) << 6);
			cp |= (*buf2++ & 0x3F);
		}

	} while ((*buf1++ == cp) && (--cp_count));

	return (*--buf1) - cp;
}

String::size_type String::find_codepoint( const std::string& str, utf32 code_point ) const
{
	String::size_type idx = 0, sze = (String::size_type)str.size();

	while (idx != sze)
	{
		if (code_point == static_cast<utf32>(static_cast<unsigned char>(str[idx])))
			return idx;

		++idx;
	}

	return npos;
}

String::size_type String::find_codepoint( const utf8* str, String::size_type len, utf32 code_point ) const
{
	String::size_type idx = 0;

	utf32	cp;
	utf8	cu;

	while (idx != len) {
		cu = *str++;

		if (cu < 0x80)
		{
			cp = (utf32)(cu);
		}
		else if (cu < 0xE0)
		{
			cp = ((cu & 0x1F) << 6);
			cp |= (*str++ & 0x3F);
		}
		else if (cu < 0xF0)
		{
			cp = ((cu & 0x0F) << 12);
			cp |= ((*str++ & 0x3F) << 6);
			cp |= (*str++ & 0x3F);
		}
		else
		{
			cp = ((cu & 0x07) << 18);
			cp |= ((*str++ & 0x3F) << 12);
			cp |= ((*str++ & 0x3F) << 6);
			cp |= (*str++ & 0x3F);
		}

		if (code_point == cp)
			return idx;

		++idx;
	}

	return npos;
}

String::size_type String::find_codepoint( const char* chars, String::size_type chars_len, utf32 code_point ) const
{
	for (String::size_type idx = 0; idx != chars_len; ++idx)
	{
		if (code_point == static_cast<utf32>(static_cast<unsigned char>(chars[idx])))
			return idx;
	}

	return npos;
}

































































































































//////////////////////////////////////////////////////////////////////////
// Comparison operators
//////////////////////////////////////////////////////////////////////////
bool	operator==(const String& str1, const String& str2)
{
	return (str1.compare(str2) == 0);
}

bool	operator==(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) == 0);
}

bool	operator==(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) == 0);
}

bool	operator==(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) == 0);
}

bool	operator==(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) == 0);
}


bool	operator!=(const String& str1, const String& str2)
{
	return (str1.compare(str2) != 0);
}

bool	operator!=(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) != 0);
}

bool	operator!=(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) != 0);
}

bool	operator!=(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) != 0);
}

bool	operator!=(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) != 0);
}


bool	operator<(const String& str1, const String& str2)
{
	return (str1.compare(str2) < 0);
}

bool	operator<(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) < 0);
}

bool	operator<(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) >= 0);
}

bool	operator<(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) < 0);
}

bool	operator<(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) >= 0);
}


bool	operator>(const String& str1, const String& str2)
{
	return (str1.compare(str2) > 0);
}

bool	operator>(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) > 0);
}

bool	operator>(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) <= 0);
}

bool	operator>(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) > 0);
}

bool	operator>(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) <= 0);
}


bool	operator<=(const String& str1, const String& str2)
{
	return (str1.compare(str2) <= 0);
}

bool	operator<=(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) <= 0);
}

bool	operator<=(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) >= 0);
}

bool	operator<=(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) <= 0);
}

bool	operator<=(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) >= 0);
}


bool	operator>=(const String& str1, const String& str2)
{
	return (str1.compare(str2) >= 0);
}

bool	operator>=(const String& str, const std::string& std_str)
{
	return (str.compare(std_str) >= 0);
}

bool	operator>=(const std::string& std_str, const String& str)
{
	return (str.compare(std_str) <= 0);
}

bool	operator>=(const String& str, const utf8* utf8_str)
{
	return (str.compare(utf8_str) >= 0);
}

bool	operator>=(const utf8* utf8_str, const String& str)
{
	return (str.compare(utf8_str) <= 0);
}

//////////////////////////////////////////////////////////////////////////
// c-string operators
//////////////////////////////////////////////////////////////////////////
bool operator==(const String& str, const char* c_str)
{
	return (str.compare(c_str) == 0);
}

bool operator==(const char* c_str, const String& str)
{
	return (str.compare(c_str) == 0);
}

bool operator!=(const String& str, const char* c_str)
{
	return (str.compare(c_str) != 0);
}

bool operator!=(const char* c_str, const String& str)
{
	return (str.compare(c_str) != 0);
}

bool operator<(const String& str, const char* c_str)
{
	return (str.compare(c_str) < 0);
}

bool operator<(const char* c_str, const String& str)
{
	return (str.compare(c_str) >= 0);
}

bool operator>(const String& str, const char* c_str)
{
	return (str.compare(c_str) > 0);
}

bool operator>(const char* c_str, const String& str)
{
	return (str.compare(c_str) <= 0);
}

bool operator<=(const String& str, const char* c_str)
{
	return (str.compare(c_str) <= 0);
}

bool operator<=(const char* c_str, const String& str)
{
	return (str.compare(c_str) >= 0);
}

bool operator>=(const String& str, const char* c_str)
{
	return (str.compare(c_str) >= 0);
}

bool operator>=(const char* c_str, const String& str)
{
	return (str.compare(c_str) <= 0);
}

//////////////////////////////////////////////////////////////////////////
// Concatenation operator functions
//////////////////////////////////////////////////////////////////////////
String	operator+(const String& str1, const String& str2)
{
	String temp(str1);
	temp.append(str2);
	return temp;
}

String	operator+(const String& str, const std::string& std_str)
{
	String temp(str);
	temp.append(std_str);
	return temp;
}

String	operator+(const std::string& std_str, const String& str)
{
	String temp(std_str);
	temp.append(str);
	return temp;
}

String	operator+(const String& str, const utf8* utf8_str)
{
	String temp(str);
	temp.append(utf8_str);
	return temp;
}

String	operator+(const utf8* utf8_str, const String& str)
{
	String temp(utf8_str);
	temp.append(str);
	return temp;
}

String	operator+(const String& str, utf32 code_point)
{
	String temp(str);
	temp.append(1, code_point);
	return temp;
}

String	operator+(utf32 code_point, const String& str)
{
	String temp(1, code_point);
	temp.append(str);
	return temp;
}

String operator+(const String& str, const char* c_str)
{
	String tmp(str);
	tmp.append(c_str);
	return tmp;
}

String operator+(const char* c_str, const String& str)
{
	String tmp(c_str);
	tmp.append(str);
	return tmp;
}

//////////////////////////////////////////////////////////////////////////
// Output (stream) functions
//////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& s, const String& str)
{
	return s << str.c_str();
}

//////////////////////////////////////////////////////////////////////////
// Modifying operations
//////////////////////////////////////////////////////////////////////////
// swap the contents of str1 and str2
void	swap(String& str1, String& str2)
{
	str1.swap(str2);
}


} // End of  CEGUI namespace section
